package net.lulihu.pangolin.headless.server.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import net.lulihu.pangolin.headless.common.model.RealAddressConnect;
import net.lulihu.pangolin.headless.common.protocol.ProtocolMessage;
import net.lulihu.pangolin.headless.common.protocol.ProtocolMessageType;
import net.lulihu.pangolin.headless.common.util.ChannelTwoWayBindKit;
import net.lulihu.pangolin.headless.server.kit.ChannelMappingKit;
import net.lulihu.pangolin.headless.server.kit.ServerWaitBindChannelKit;

import java.net.InetSocketAddress;

/**
 * 用户代理通道处理器
 */
@Slf4j
public class UserProxyChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        Channel userChannel = ctx.channel();
        // 获取访问端口
        int port = ((InetSocketAddress) userChannel.localAddress()).getPort();

        if (log.isDebugEnabled()) log.debug("用户代理端口[{}]通道激活", port);

        // 获取映射消息
        ChannelMappingKit.PortMappingInfo mappingInfo = ChannelMappingKit.getMappingInfo(port);

        // 未找到代理信息 断开
        if (mappingInfo == null) {
            userChannel.close();
            if (log.isDebugEnabled()) log.debug("用户代理端口[{}]未找到映射通道，用户通道关闭", port);
            return;
        }

        // 用户连接到代理服务器时，设置用户连接不可读，等待代理后端服务器连接成功后再改变为可读状态
        userChannel.config().setOption(ChannelOption.AUTO_READ, false);
        // 设置等待绑定的通道
        String channelId = ServerWaitBindChannelKit.putWaitBindChannel(userChannel);

        // 发送与真实服务器地址建立连接消息
        RealAddressConnect addressConnect = new RealAddressConnect();
        addressConnect.setAddress(mappingInfo.getAddress());
        addressConnect.setChannelId(channelId);

        ProtocolMessage connectMessage = new ProtocolMessage();
        connectMessage.setType(ProtocolMessageType.S_REAL_CONNECT);
        connectMessage.setBodyObj(addressConnect);

        mappingInfo.getChannel().writeAndFlush(connectMessage);

        if (log.isDebugEnabled()) log.debug("发送与真实服务器地址建立连接消息");
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) {
        Channel userChannel = ctx.channel();

        if (log.isDebugEnabled()) {
            log.debug("用户代理端口[{}]通道可读", ((InetSocketAddress) userChannel.localAddress()).getPort());
        }

        // 获取绑定通道
        Channel proxyChannel = ChannelTwoWayBindKit.getBindChannel(userChannel);

        if (proxyChannel == null) {
            userChannel.close();
        } else {
            byte[] bytes = new byte[buf.readableBytes()];
            buf.readBytes(bytes);

            // 发送代理数据
            ProtocolMessage proxyData = new ProtocolMessage();
            proxyData.setType(ProtocolMessageType.TYPE_DATA_TRANSFER);
            proxyData.setBody(bytes);
            proxyChannel.writeAndFlush(proxyData);
        }
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) {
        Channel userChannel = ctx.channel();

        int port = ((InetSocketAddress) userChannel.localAddress()).getPort();
        if (log.isDebugEnabled()) {
            log.debug("用户代理端口[{}]通道可写性状态改变", ((InetSocketAddress) userChannel.localAddress()).getPort());
        }

        // 获取映射消息
        ChannelMappingKit.PortMappingInfo mappingInfo = ChannelMappingKit.getMappingInfo(port);

        if (mappingInfo == null) {
            userChannel.close();
        } else {
            Channel proxyChannel = ChannelTwoWayBindKit.getBindChannel(userChannel);

            if (proxyChannel != null)
                proxyChannel.config().setOption(ChannelOption.AUTO_READ, userChannel.isWritable());
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        Channel userChannel = ctx.channel();

        int port = ((InetSocketAddress) userChannel.localAddress()).getPort();
        if (log.isDebugEnabled()) {
            log.debug("用户代理端口[{}]通道无效", ((InetSocketAddress) userChannel.localAddress()).getPort());
        }

        // 获取映射消息
        ChannelMappingKit.PortMappingInfo mappingInfo = ChannelMappingKit.getMappingInfo(port);

        if (mappingInfo == null) {
            userChannel.close();
        } else {
            Channel proxyChannel = ChannelTwoWayBindKit.getBindChannel(userChannel);
            if (proxyChannel != null && proxyChannel.isActive()) {
                proxyChannel.config().setOption(ChannelOption.AUTO_READ, true);

                // 告诉客户端通道断开
                ProtocolMessage message = new ProtocolMessage();
                message.setType(ProtocolMessageType.TYPE_DISCONNECT);
                proxyChannel.writeAndFlush(message);
                proxyChannel.close();
            }
            // 解除绑定
            ChannelTwoWayBindKit.unbind(userChannel, proxyChannel);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.close();
        log.error("", cause);
    }
}
